package pl.edu.agh.broker.mns.server.internal;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import pl.edu.agh.broker.mns.shared.MultiNameService;
import pl.edu.agh.broker.mns.shared.Respondable;


public class SimpleMultiNameService extends UnicastRemoteObject implements
		MultiNameService {

	private static final long serialVersionUID = 5163284454005981387L;

	private class RespondThread extends Thread {

		private final static long SLEEP_TIME = 60 * 1000;

		private boolean running;

		public RespondThread() {
			this.running = true;
		}

		@Override
		public void run() {
			while (running) {
				try {
					Thread.sleep(SLEEP_TIME);
				} catch (InterruptedException ex) {
					continue;
				}
				synchronized (SimpleMultiNameService.this) {

					List<RespondableObject> notResponding = new ArrayList<RespondableObject>();
					for (RespondableObject v : multiMap) {
						try {
							v.getRespondable().respond();
						} catch (RemoteException e) {
							notResponding.add(v);
						}
					}
					for (RespondableObject v : notResponding) {
						System.out.println("\t...removed " + v);
						multiMap.remove(v.getKey(), v);
					}
				}
			}
		}

		public void end() {
			running = false;
			RespondThread.this.interrupt();
		}
	}

	private final MultiMap<String, RespondableObject> multiMap;
	private final StartStopListener[] listeners;
	private RespondThread respondThread;

	public SimpleMultiNameService(StartStopListener... listeners)
			throws RemoteException {
		this.listeners = listeners;
		multiMap = new MultiMap<String, RespondableObject>();
	}

	public void start() {
		respondThread = new RespondThread();
		respondThread.start();
		for (StartStopListener l : listeners) {
			l.onStart();
		}
	}

	public void stop() {
		respondThread.end();
		for (StartStopListener l : listeners) {
			l.onStop();
		}
	}

	@Override
	public void replaceAllBy(String key, Serializable value,
			Respondable responder) {
		synchronized (this) {
			multiMap.remove(key);
			multiMap.put(key, new RespondableObject(key, value, responder));
		}
	}

	@Override
	public void remove(String key, Serializable value) {
		synchronized (this) {
			for (RespondableObject v : multiMap.get(key)) {
				if (v.getObject().equals(value)) {
					multiMap.remove(key, v);
				}
			}
		}
	}

	@Override
	public void removeAll(String key) {
		synchronized (this) {
			multiMap.remove(key);
		}
	}

	@Override
	public void add(String key, Serializable value, Respondable responder)
			throws RemoteException {
		synchronized (this) {
			multiMap.put(key, new RespondableObject(key, value, responder));
		}
	}

	@Override
	public Collection<Serializable> get(String key) throws RemoteException {
		synchronized (this) {
			Collection<RespondableObject> wrappers = multiMap.get(key);
			Collection<Serializable> ret = new ArrayList<Serializable>();
			for (RespondableObject w : wrappers) {
				ret.add(w.getObject());
			}
			return ret;
		}
	}

	@Override
	public Serializable getAny(String key) throws RemoteException {
		synchronized (this) {
			Collection<RespondableObject> collection = multiMap.get(key);
			Serializable ret = null;
			for (RespondableObject c : collection) {
				ret = c.getObject();
				break;
			}
			return ret;
		}
	}
}
